package com.spider.moudle.factory;

import com.google.common.collect.Lists;
import com.spider.moudle.constant.Constant;
import com.spider.moudle.entity.Cookie;
import com.spider.moudle.util.PropertiesUtil;
import org.apache.http.*;
import org.apache.http.client.CookieStore;
import org.apache.http.client.HttpRequestRetryHandler;
import org.apache.http.client.config.CookieSpecs;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.config.SocketConfig;
import org.apache.http.conn.ConnectTimeoutException;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.DefaultHttpRequestRetryHandler;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicHeader;
import org.apache.http.protocol.HttpContext;
import org.apache.log4j.PropertyConfigurator;
import us.codecraft.webmagic.downloader.CustomRedirectStrategy;

import javax.net.ssl.*;
import java.io.IOException;
import java.io.InterruptedIOException;
import java.net.UnknownHostException;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Collection;
import java.util.List;

/**
 * @author 夏茂轩
 * @description 自定义httpclient工厂类
 * @date 2017/12/8 9:01
 */
public class HttpClientFactory {
    private static PoolingHttpClientConnectionManager clientConnectionManager = null;
    private static PoolingHttpClientConnectionManager localPool=null;
    private static RequestConfig config = RequestConfig.custom().
            setConnectTimeout(Constant.HTTP_CONNECT_TIME_OUT).
            setSocketTimeout(Constant.HTTP_SOCKET_TIME_OUT)
            .setConnectionRequestTimeout(Constant.HTTP_CONNECTION_REQUEST_TIME_OUT)
            .setCookieSpec(CookieSpecs.STANDARD_STRICT).build();
    private static HttpClientBuilder builder = HttpClients.custom();
    private static SocketConfig socketConfig;
    private static Collection<Header> defaultHeaders = Lists.newArrayList(new BasicHeader("User-Agent", Constant.HTTP_USER_AGENT)
            , new BasicHeader("Accept-Encoding", Constant.ACCEPT_ENCODING));
    private static HttpRequestRetryHandler httpRequestRetryHandler = new HttpRequestRetryHandler() {
        @Override
        public boolean retryRequest(IOException exception, int executionCount, HttpContext context) {
            if (executionCount >= Constant.HTTP_RETRY_TIMES) {
                return false;
            }
            if (exception instanceof NoHttpResponseException) {
                return true;
            }
            if (exception instanceof SSLHandshakeException) {
                return false;
            }
            if (exception instanceof InterruptedIOException) {
                return false;
            }
            if (exception instanceof UnknownHostException) {
                return false;
            }
            if (exception instanceof ConnectTimeoutException) {
                return false;
            }
            if (exception instanceof SSLException) {
                return false;
            }
            HttpClientContext clientContext = HttpClientContext.adapt(context);
            HttpRequest request = clientContext.getRequest();
            // 如果请求是幂等的，就再次尝试
            if (!(request instanceof HttpEntityEnclosingRequest)) {
                return true;
            }
            return false;
        }
    };

    public static RequestConfig buildRequestConfig(HttpHost proxy) {
        RequestConfig.Builder requestConfigBuilder = RequestConfig.custom();
        RequestConfig config;
        if (proxy != null) {
            config = requestConfigBuilder.setConnectTimeout(Constant.HTTP_CONNECT_TIME_OUT).
                    setSocketTimeout(Constant.HTTP_SOCKET_TIME_OUT)
                    .setConnectionRequestTimeout(Constant.HTTP_CONNECTION_REQUEST_TIME_OUT)
                    .setCookieSpec(CookieSpecs.STANDARD_STRICT).setProxy(proxy).build();
        } else {
            config = requestConfigBuilder.setConnectTimeout(Constant.HTTP_CONNECT_TIME_OUT).
                    setSocketTimeout(Constant.HTTP_SOCKET_TIME_OUT)
                    .setConnectionRequestTimeout(Constant.HTTP_CONNECTION_REQUEST_TIME_OUT)
                    .setCookieSpec(CookieSpecs.STANDARD_STRICT).build();
        }
        return config;
    }

    public static RequestConfig buildRequestConfig(HttpHost proxy, int timeOut) {
        RequestConfig.Builder requestConfigBuilder = RequestConfig.custom();
        RequestConfig config;
        if (proxy != null) {
            config = requestConfigBuilder.setConnectTimeout(timeOut).
                    setSocketTimeout(timeOut)
                    .setConnectionRequestTimeout(timeOut)
                    .setCookieSpec(CookieSpecs.STANDARD_STRICT).setProxy(proxy).build();
        } else {
            config = requestConfigBuilder.setConnectTimeout(timeOut).
                    setSocketTimeout(timeOut)
                    .setConnectionRequestTimeout(timeOut)
                    .setCookieSpec(CookieSpecs.STANDARD_STRICT).build();
        }
        return config;
    }


    static {
        try {
            //初始化日志环境
            PropertyConfigurator.configure(PropertiesUtil.getLogPro());
        } catch (IOException e) {
            e.printStackTrace();
        }
        Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory>create()
                .register("https", buildSSLConnectionSocketFactory())
                .register("http", PlainConnectionSocketFactory.INSTANCE)
                .build();
        SocketConfig.Builder socketConfigBuilder = SocketConfig.custom();
        socketConfigBuilder.setSoKeepAlive(true).setTcpNoDelay(true);
        socketConfigBuilder.setSoTimeout(Constant.HTTP_CONNECT_TIME_OUT);
        socketConfig = socketConfigBuilder.build();
        clientConnectionManager = new PoolingHttpClientConnectionManager(socketFactoryRegistry);
        localPool= new PoolingHttpClientConnectionManager(socketFactoryRegistry);
        clientConnectionManager.setDefaultSocketConfig(socketConfig);
        localPool.setDefaultSocketConfig(socketConfig);
        clientConnectionManager.setMaxTotal(Constant.MAX_CONNECTION);
        localPool.setMaxTotal(Constant.MAX_CONNECTION);
        clientConnectionManager.setDefaultMaxPerRoute(Constant.MAX_CONNECTION);
        localPool.setDefaultMaxPerRoute(Constant.MAX_CONNECTION);
    }

    public static CloseableHttpClient buildHttpClient(HttpHost prxoy) {
        CloseableHttpClient httpClient = builder.setDefaultRequestConfig(config)
                .setConnectionManager(clientConnectionManager).setDefaultHeaders(defaultHeaders)
                .setRetryHandler(httpRequestRetryHandler).setProxy(prxoy).build();
        return httpClient;
    }

    public static CloseableHttpClient buildHttpClient() {
        CloseableHttpClient httpClient = builder.setDefaultRequestConfig(config)
                .setConnectionManager(clientConnectionManager).setDefaultHeaders(defaultHeaders)
                .setRetryHandler(httpRequestRetryHandler).build();
        return httpClient;
    }

    public static CloseableHttpClient buildHttpClient(boolean isProxy) {
        HttpClientBuilder builder = HttpClients.custom();
        builder.setRedirectStrategy(new CustomRedirectStrategy());
        builder.setDefaultSocketConfig(socketConfig);
        builder.setRedirectStrategy(new CustomRedirectStrategy());
        builder.setRetryHandler(new DefaultHttpRequestRetryHandler(Constant.HTTP_RETRY_TIMES, true));
        CloseableHttpClient httpClient = null;
        if (isProxy) {
            httpClient = builder.setDefaultRequestConfig(config)
                    .setConnectionManager(clientConnectionManager)
                    .setRetryHandler(httpRequestRetryHandler).build();
        } else {
            httpClient = builder.setDefaultRequestConfig(config)
                    .setRetryHandler(httpRequestRetryHandler).build();
        }
        return httpClient;
    }

    private static SSLConnectionSocketFactory buildSSLConnectionSocketFactory() {
        try {
            // 优先绕过安全证书
            return new SSLConnectionSocketFactory(createIgnoreVerifySSL());
        } catch (KeyManagementException e) {
            e.printStackTrace();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        return SSLConnectionSocketFactory.getSocketFactory();
    }

    private static SSLContext createIgnoreVerifySSL() throws NoSuchAlgorithmException, KeyManagementException {
        // 实现一个X509TrustManager接口，用于绕过验证，不用修改里面的方法
        X509TrustManager trustManager = new X509TrustManager() {

            @Override
            public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
            }

            @Override
            public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
            }

            @Override
            public X509Certificate[] getAcceptedIssuers() {
                return null;
            }

        };

        SSLContext sc = SSLContext.getInstance("SSLv3");
        sc.init(null, new TrustManager[]{trustManager}, null);
        return sc;
    }

}
